#include <iostream>

#include "codec.h"
#include "dispatcher.h"
#include "query.pb.h"
#include "xlcomm/net/tcp_server.h"

using namespace xlcomm;

class QueryServer {
 public:
  QueryServer(net::EventLoop* loop, const net::InetAddress& listen_addr)
      : loop_(loop),
        server_(loop, listen_addr, "QueryServer"),
        dispatcher_(),
        codec_(std::bind(&ProtobufDispatcher::OnProtobufMessage, &dispatcher_,
                         _1, _2, _3)) {
    server_.set_connect_callback(
        std::bind(&QueryServer::OnConnection, this, _1));
    server_.set_disconnect_callback(
        std::bind(&QueryServer::OnConnection, this, _1));
    server_.set_message_callback(
        std::bind(&ProtobufCodec::OnMessage, &codec_, _1, _2, _3));
    dispatcher_.RegisterMessageCallback<Query>(
        std::bind(&QueryServer::OnQuery, this, _1, _2, _3));
  }

  void Start() { server_.Start(0); }

 private:
  using ConnectionList = std::set<net::TcpConnectionPtr>;

  void OnConnection(const net::TcpConnectionPtr& conn) {
    if (conn->IsConnected()) {
      connections_.insert(conn);
    } else {
      connections_.erase(conn);
    }
  }

  void OnQuery(const net::TcpConnectionPtr& conn,
               const std::shared_ptr<Query>& query, Timestamp recv_time) {
    std::cout << "QueryServer::OnQuery----";
    std::string type_name = query->GetTypeName();
    std::cout << " type_name:" << type_name;

    XLCOMM_ASSERT(strcmp(type_name.c_str(), "xlcomm.Query") == 0);
    std::cout << " id:" << query->id() << " questioner:" << query->questioner()
              << " question:" << query->question() << std::endl;

    Answer answer;
    answer.set_id(query->id());
    answer.set_questioner(query->questioner());
    // answer.set_answerer("ubuntu");
    answer.set_slotion("My name is Ming");
    codec_.Send<Answer>(conn, answer);
  }

  net::EventLoop* loop_;
  net::TcpServer server_;
  ConnectionList connections_;
  ProtobufDispatcher dispatcher_;
  ProtobufCodec codec_;
};

int main() {
  net::EventLoop loop;
  QueryServer server(&loop, net::InetAddress(30002));
  server.Start();
  loop.Loop();
  return 0;
}